---
title: Gradle构建过程
---
##  Gradle构建过程
gradle 脚本的执行，是指 gradle 编译那些 gradle 脚本文件，生成对应的对象和 Task 任务。

Gradle 的构建过程是通用的，任何由 Gradle 构建的项目都遵循这个过程。

Gradle 构建分为三个阶段，每个阶段都有自己的职责，每个阶段都完成一部分任务，前一阶段成果是下一阶段继续执行的前提：

* Initialization --> 初始化阶段，按顺序执行 init.gradle -> settings.gradle 脚本文件，生成 Gradle、Setting、Project 对象。
* Configuration --> 编译阶段，也叫配置阶段。按顺序执行 build.gradle -> *.gradle 脚本文件，完成项目的编译。
* Execution --> 执行阶段，按顺序执行 Task 任务，完成项目的执行。



整个构建过程，官方在一些节点都设置有 hook 钩子函数，可以在这些节点插入自定义的逻辑，影响构建过程。hook 钩子函数可以理解为监听函数。



### Initialization 阶段

Initialization 是初始化阶段，一上来就会马上把全局的 Gradle 内置对象 new 出来，Gradle 对象中的参数都是本次 gradle 构建的全局属性，通过 Gradle 对象可以干很多事。

* 比如可以获取 Gradle Home、Gradle User Home 目录。
* 添加全局监听。
* 给所有项目添加设置、依赖等。

Initialization 阶段会按照先后顺序运行 2 个 Groovy 脚本：

* Init Script ： 创建内置对象 Gradle
* Setting Script： 创建内置对象 Setting、每个 module 对应的 Project。

### Configuration 阶段

Initialization 阶段后就是 Configuration 阶段了，Configuration 阶段会执行所有的 build.gradle 脚本，先执行根目录即项目的构建脚本，再根据子项目之间的依赖关系，挨个执行子项目的构建脚本。



Gradle 中有根项目和子项目之分，不管是什么项目在 Gradle 中都对应一个 Project 对象

根项目只有一个，而子项目可以有多个，android 中即使是 app module 其实也是一个子项目。



Gradle 默认的根项目是空的，没有内容的，根项目只有 .gradle 有实际意义，其它的都没用。



Gradle 中每一个项目都必须有一个 .gradle 构建脚本，Configuration 阶段会执行所有项目的构建脚本，从根项目开始一直把所有参与本地构建的子项目构建脚本都执行完，在这个过程中，会根据 .gradle 构建脚本内容，创建对应的 Project 对象，在后面的环节，1 个 Project 对象就对应着 1 个项目。rootProject 表示根项目， Project 表示子项目。

:::tip

.gradle 脚本里的 DSL 配置块都是 Project 对象的方法而与。

:::

然后根据脚本中的配置，生成 Configuration 阶段的最终产物：Task 有向无环图，给下一阶段执行。

Configuration 阶段的目的就是根据脚本配置计算出整个构建过程需要的逻辑和流程，可以理解为动态生成代码的过程。



### Execution阶段

Execution 阶段就是拿到 Configuration 阶段计算出来的 task 执行图，按照顺序一个个跑这些 task 就完成构建了。

## Gradle Task

Task 是 Gradle 执行的基本单元。

Gradle 构建项目的流程，是先把所有的 `.gradle`脚本执行一遍，编译生成对应的 Gradle 、Setting、Project 对象，然后根据构建脚本中设置的构建配置，生成一张 Task 组成的：有环无向图

![image-20230527142325367](https://img.wkq.pub/img/image-20230527142325367.png)

最终，Gradle 会按照图上一个个 Task 的关联顺序挨个执行，每一个 Task 都完成一个特定功能，按照顺序把 Task 图执行一遍，整个项目的构建任务就完成了。



### 什么是 Task

:::tip

Gradle 中，每一个待编译的工程都叫一个 Project。每一个 Project 在构建的时候都包含一系列的 Task。比如一个  Android  APK 的编译可能包含：Java 源码编译 Task、资源编译 Task、JNI 编译 Task、lint 检查 Task 、打包生成 APK 的 Task、签名 Task 等。一个 Project 到底包含多少个 Task ，其实由编译脚本指定的插件决定。插件是什么呢？插件就是用来定义 Task ，并具体执行这些 Task 的东西。

:::

Task 我们可以看成一个个任务，这些任务可以是编译 java 代码、编译 C/C++ 代码、编译资源文件生成对应的 R 文件、打包生成 jar、aar库文件、签名、混淆、图片压缩、打包生成  APK 文件、包/资源发布等。

下图中这些都是一个个 Task 任务，每一个 Task 任务都是为了实现某个目的，可以理解为一个步骤，最终一个个步骤执行完就完成了整个项目的构建。



![image-20230527143717866](https://img.wkq.pub/img/image-20230527143717866.png)

Task 是完成一类任务，实际上对应的也是一个对象 。而Task 是由无数 Action 组成的， Action 代表的是一个个函数、方法 ，每个 Task 都是一堆 Action 按序组成的执行图，就像我们在 Class 的 main 函数中按照逻辑调用一系列方法一样。

例如，现在有个需求，要在打包出 jar 的时候顺便看看 jar 文件的大小，在 gradle 中仅需要在构建脚本中编写几行代码即可，在其中我们可以以编程方式自定义一些构建任务，因为使用了编程方式，所以这给我们带来了极大的灵活性和便捷性；而在 Maven 中需要编写 maven 插件，复杂程度完全不在一个水平。

虽然 gradle 可以非常灵活的编写自定义脚本任务，但是其实一般情况下我们不需要编写构建脚本，利用现有插件和任务即可完成相关功能。

### 创建 Task

```groovy
task("task1") {
    print "hello task1"
}
task(hello2) {
    print "hello task2"
}

task hello {
    doLast {
        println 'Hello world!'
    }
}
```

### 运行 Task

命令行执行 gradle  hello

### Task 的 action

上面说的 Task 内部的 action 也不是唯一的，而是一个集合，我们可以往里面添加各种 action，要注意 action 之间有前后执行顺序的，这个是规定好的。这些 action 接收都是闭包：





```groovy
task speak{
    println("I have nothing to say...")
    doFirst {
        println("This is doFirst! --------> inner")
    }
    doLast {
        println("This is doLast!")
    }
}
speak.doFirst {
    println("This is doFirst! --------> outer")
}
```

doFirst {...}、doLast {...} 接收的都是闭包

* doFirst 添加的 action 最先执行
* task 自身的 action 在中间执行，这个无法在 task 外部添加，可以在自定义 task 时写。
* doLast 添加的 action 最后执行

另外 task 也可以像面向对象那样操作， task 里面也可以写代码，比如打印 AA，但是这些代码只能在配置阶段执行，而 task 的 action 都是在运行阶段执行的。

```groovy
task speak{
    // task 的配置阶段：配置阶段执行
    println("I have nothing to say...")
    // task 的执行阶段：执行阶段执行
    doFirst {
        println("This is doFirst! --------> inner")
    }
    doLast {
        println("This is doLast!")
    }
}
speak.doFirst {
    println("This is doFirst! --------> outer")
}
```

### 任务之间共享数据

```java
ext {
    name = 'AAA'
}
def age = 19

task s1 {
    doLast{
        age = 12
        rootProject.ext.name = "BBB"
        println("s1: " + rootProject.ext.name)
    }
}
task s2 {
    doLast {
        println("s2 age: " + age)
        println("s2: " + rootProject.ext.name)
    }
}
```

在做微服务项目的时候，可以做一些版本的统一。

### Task 依赖

Task 依赖是指我们可以指定  Task 之间的执行顺序，重点理解 depensOn 。

* A.dependsOn B ---> 执行 A 的时候先执行 B

* A.mustRunAfter B ---> 同时执行 A和 B，先执行  B 再执行 A，若执行关系不成立则报错。

* A.shouldRunAfter B ---> 同 mustRunAfter ，但是执行关系不成立不会报错。



```groovy
task s1 {
    doLast {
        age = 12
        rootProject.ext.name = "BBB"
        println("s1: " + rootProject.ext.name)
    }
}
task s2 {
    dependsOn(s1)
    doLast {
        println("s2 age: " + age)
        println("s2: " + rootProject.ext.name)
    }
}

s1.dependsOn s2
```



### 自定义 Task

Gradle 中 Task 都是继承自 DefaultTask，我们自定义 Task 也需要继承这个类，重点是写自己需要的方法，然后加上 @TaskAction 注解表示这个方法是 Task 中的 action，可以加多个，按照顺序执行

 ```groovy
 //自定义一个创建 Java 工程目录的任务，当目录存在时不进行创建，不存在时进行创建
 //1. 创建闭包
 def createDir = { path ->
     def dir = new File(project.projectDir, path)
     if (!dir.exists()) {
         println("创建目录：${path}")
         dir.mkdirs()
     }
 }
 
 //2. 创建任务
 task makeJavaDir() {
     //指定创建的目录结构
     def paths = ['src/main/java', 'src/main/resources', 'src/test/java', 'src/test/resources', "src/main/java/${project.group.replace('.', '/')}/${project.name}"]
 
     //在任务列表前添加一个任务
     doFirst {
         paths.forEach(createDir)
     }
 }
 
 ```

### 系统默认 Task

gradle 默认提供了很多 task 供我们使用，比如 copy、delete

#### 1. copy

```groovy
task speak(type: Copy) {
    
}
```

```groovy
task speak(type: Copy) {
    //数据源目录，多个目录
    from 'src/main/java'
    //目标目录，单一
    into 'src/main/java'
    //过滤文件，包含
    include '**/*.java'
    //过滤文件、排除
    exclude '**/Test*.java'
    //重新命名，老名字、新名字
    rename '(.+)\\.java', '$1.txt'
    //删除文件 Project 接口
    delete 'src/main/java/pub/wkq/gradle/demo2/HelloWorld.java'
}
```

### 设置默认的 Task 任务

设置这个的意思是指，脚本中我们不调用该 task ,设置的 task 也会执行

## Gradle Plugin

### 什么是插件

**插件只是一组任务**，几乎所有任务，如编译任务，设置域对象，设置源文件等都由插件处理。

Gradle 在它的核心中提供了一些小但有用的功能。其它所有有用的功能，都是通过插件进行添加的。

### 应用插件

```groovy title='应用插件：在 build.gradle 文件中'
apply plugin: 'java'
```

插件都有一个短名称，通过短名称取应用 JavaPlugin。

或者是 ：

```groovy
plugins {
    id 'java'
}
```

插件的应用是幂等的。也就是说，一个插件可以被应用多次。



一个插件是任何实现了 Plugin 接口的简单的类。Gradle 提供了核心插件作为其发行包的一部分，所以只要学习用法就可以了。



然而，对于第三方插件，需要进行配置以使插件在构建类路径中可用。

### 插件都做了什么
把插件应用到项目中可以让插件来扩展项目的功能。它可以做的事情如:
* 将任务添加到项目（如编译、测试）。
* 使用有用的默认设置对已添加的任务进行预配置。
* 向项目中添加依赖配置。
* 通过扩展对现有类型添加新的属性和方法。

#### 通过插件添加任务

```groovy title='build.gradle'
apply plugin: 'java'
task show {
    println relativePath(compileJava.destinationDir)
    println relativePath(compileResources.destinationDir)
}
```

gradle -q show 的输出结果：

```shell
>gradle -q show
build/classes/main
build/resources/main
```

Java 插件已经向项目添加了 compileJava 任务 和  processResources 任务，并且配置了这两个任务的 destinationDir 属性。

## Gradle搭建微服务项目
```groovy title='build.gradle'
buildscript { //构建 gradle 脚本自身需要的资源，可以声明的资源包括依赖项，第三方插件、maven 仓库地址等
    ext {
        //统一版本管理
        lombokVersion = '1.18.8'
        springBootVersion = '3.0.0'
        springCloudVersion = '2022.0.0'
        springCloudAlibabaVersion = '2022.0.0.0-RC1'
        mybatisSpringBootVersion = '3.0.0'
        mysqlVersion = '8.0.30'
    }
    //设置仓库地址
    repositories {
        //从前到后顺序执行，找不到就往后找
        mavenLocal()//本地仓库
        maven { url 'https://maven.aliyun.com/repository/public' }
        mavenCentral()//官方仓库
    }
    dependencies {
        //spring-boot-gradle-plugin,方便版本管理
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
//全局配置，包括 root 及子项目
allprojects {
    apply plugin: 'java'
    group = 'pub.wkq'
    version = '1.0-SNAPSHOT'
    sourceCompatibility = 17 //Java 版本
    targetCompatibility = 17 //Java版本
    tasks.withType(JavaCompile) {
        options.encoding = "UTF-8"
    }
    repositories {
        //从前到后顺序执行，找不到就往后找
        mavenLocal()//本地仓库
        maven { url 'https://maven.aliyun.com/repository/public' }
        mavenCentral()//官方仓库
    }
}
//配置所有子项目
subprojects {
    apply plugin: 'io.spring.dependency-management'//版本管理插件
    //dependencyManagement版本统一管理，类似maven的dependencyManagement
    dependencies {
        annotationProcessor 'org.projectlombok:lombok'//注释处理器
        implementation 'org.projectlombok:lombok'//引入lombok依赖
    }
    dependencyManagement {
        dependencies {
            //统一版本管理
            dependency "org.projectlombok:lombok:${lombokVersion}"
            dependency "mysql:mysql-connector-java:${mysqlVersion}"
            dependency "org.mybatis.spring.boot:mybatis-spring-boot-starter:${mybatisSpringBootVersion}"
        }
        imports {
            mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"//Spring Boot
            mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"//Spring Cloud
            mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${springCloudAlibabaVersion}"//Spring Cloud Alibaba
        }
    }
}
project('user-service') {
    apply plugin: 'org.springframework.boot'
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
        implementation 'mysql:mysql-connector-java'
        implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }
}
project('order-service') {
    apply plugin: 'org.springframework.boot'
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
        implementation 'mysql:mysql-connector-java'
        implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }
}
```